﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Components.Anexo
{
  [ToolboxBitmap(typeof(OpenFileDialog)), DefaultBindingProperty("DataSource")]
  public partial class FileAttach : UserControl
  {
    public FileAttach()
    {
      InitializeComponent();
      dtArquivos.Columns.Add("FileName", typeof(string));
      dtArquivos.Columns.Add("Arquivo", typeof(byte[]));
      dtArquivos.PrimaryKey = new DataColumn[] { dtArquivos.Columns["FileName"] };
    }

    #region Variáveis

    List<byte[]> files = new List<byte[]>();
    DataTable dtArquivos = new DataTable();
    byte[] dataSource = null;
    bool zipFile;
    int _compactLevel;

    #endregion



    #region Propriedades

    [Description("Lista de arquivos.")]
    [Browsable(false)]
    public List<byte[]> Files
    {
      get { return files; }
    }

    public byte[] DataSource
    {
      get { return dataSource; }
      set
      {
        this.dataSource = value;
        //Limpa os arquivos antes de adicionar os itens
        listView1.Items.Clear();
        files.Clear();
        this.dtArquivos.Rows.Clear();

        if (this.dataSource != null)
        {
          //Carrega o DataTable
          byte[] arqs = this.dataSource;
          MemoryStream ms = new MemoryStream(arqs, true);
          System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
          this.dtArquivos = (DataTable)bin.Deserialize(ms);
          AddItens();
        }
      }
    }

    [Category("Compactação")]
    [Description("Define se o arquivo vai ser zipado antes da inserção.")]
    public bool ZipFile
    {
      get { return zipFile; }
      set { zipFile = value; }
    }

    [Category("Compactação")]
    [Description("Define o nível da compactação [0-9].")]
    [DefaultValue(6)]
    public int CompactingLevel
    {
      get { return _compactLevel; }
      set { _compactLevel = value; }
    }

    public System.Windows.Forms.ListView ListView
    {
      get { return listView1; }
      set { listView1 = value; }
    }

    private void AddItens()
    {
      foreach (DataRow row in dtArquivos.Rows)
      {
        byte[] arq = (byte[])row["Arquivo"];
        files.Add(arq);
        string fileName = row["FileName"].ToString();
        ListViewItem item = new ListViewItem(fileName);
        item.Name = fileName;
        string size = string.Empty;
        if (arq.Length < 1024)
          size = arq.Length + " Bytes";
        else
          if (arq.Length >= 1024 && arq.Length < 1048576)
          {
            size = string.Format("{0:N3}", ((double)arq.Length / 1024)) + " KBytes";
          }
          else
            if (arq.Length >= 1048576 && arq.Length < 1073741824)
            {
              size = string.Format("{0:N3}", ((double)arq.Length / 1048576)) + " MBytes";
            }
            else
            {
              size = string.Format("{0:N3}", ((double)arq.Length / 1073741824)) + " GBytes";
            }

        item.SubItems.Add(size);
        this.listView1.Items.Add(item);
      }
    }

    public ToolStripButton InsertButton
    {
      get { return this.tsbInserir; }
    }

    public ToolStripButton DeleteButton
    {
      get { return this.tsbExcluir; }
    }

    public ToolStripButton OpenButton
    {
      get { return this.tsbAbrir; }
    }

    public ToolStripButton ExportButton
    {
      get { return this.tsbExportar; }
    }

    #endregion



    #region Eventos Padrões

    private void tsbInserir_Click(object sender, EventArgs e)
    {
      BeforeInsertEventArgs eventArgs = new BeforeInsertEventArgs();
      if (BeforeInsert != null)
        BeforeInsert((ToolStripButton)sender, eventArgs);

      if (eventArgs.Cancel)
        return;

      OpenFileDialog fd = new OpenFileDialog();
      fd.ShowDialog();
      foreach (string path in fd.FileNames)
      {
        Insert(path);
      }

      if (AfterInsert != null)
        AfterInsert((ToolStripButton)sender, new EventArgs());
    }

    private void tsbExcluir_Click(object sender, EventArgs e)
    {
      if (listView1.SelectedItems.Count == 0 || MessageBox.Show("Deseja realmente excluir esse item?", "Atenção", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.No)
        return;

      //*****************Tratamento BeforeDelete*********************
      BeforeDeleteEventArgs eventArgs = new BeforeDeleteEventArgs(new Arquivo(listView1.SelectedItems[0].Text, (byte[])dtArquivos.Rows.Find(listView1.SelectedItems[0].Text)["Arquivo"]));
      if (BeforeDelete != null)
        BeforeDelete((ToolStripButton)sender, eventArgs);

      if (eventArgs.Cancel)
        return;
      //*************************************************************



      if (listView1.SelectedItems.Count == 0)
        return;
      Remove(listView1.SelectedItems[0].Text);



      //******************Tratamento AfterDelete*********************

      if (AfterDelete != null)
        AfterDelete((ToolStripButton)sender, new EventArgs());

      //*************************************************************
    }

    private void tsbExportar_Click(object sender, EventArgs e)
    {
      if (listView1.SelectedItems.Count == 0)
        return;
      SaveFileDialog sfd = new SaveFileDialog();
      sfd.AddExtension = true;
      sfd.DefaultExt = Path.GetExtension(listView1.SelectedItems[0].Text);
      sfd.Filter = "Arquivo (*" + sfd.DefaultExt + ")|*" + sfd.DefaultExt;
      sfd.ShowDialog();
      if (sfd.FileName.Equals(string.Empty))
        return;
      Export(sfd.FileName, listView1.SelectedItems[0].Text);
    }

    private void tsbAbrir_Click(object sender, EventArgs e)
    {
      if (listView1.SelectedItems.Count == 0)
        return;
      Open(listView1.SelectedItems[0].Text);
    }

    private void listView1_DoubleClick(object sender, EventArgs e)
    {
      if (listView1.SelectedItems.Count == 0)
        return;
      Open(listView1.SelectedItems[0].Text);
    }

    #endregion



    #region Métodos Internos do Objeto

    private void RefreshDataSource()
    {
      string tempPath = Path.GetTempFileName();
      FileStream fs = new FileStream(tempPath, FileMode.Append);
      System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
      bin.Serialize(fs, dtArquivos);

      fs.Close();
      this.DataSource = File.ReadAllBytes(tempPath);
      fs.Dispose();
    }

    #endregion



    #region Métodos públicos do Objeto

    public bool Insert(string path)
    {
      try
      {
        string realPath = path;
        byte[] arq;
        string tempPath = Path.GetTempFileName();
        FileStream fs = new FileStream(tempPath, FileMode.Append);
        if (zipFile && !ZipUtility.IsZip(realPath))
        {
          realPath = Path.GetTempPath() + Path.GetFileName(path.Remove(path.LastIndexOf(Path.GetExtension(path))) + ".zip");
          ZipUtility.ZipFile(path, _compactLevel, false, fs);
          fs.Close();
          arq = File.ReadAllBytes(tempPath);
          fs.Dispose();
        }
        else
          arq = File.ReadAllBytes(path);//File.ReadAllBytes(realPath);

        dtArquivos.Rows.Add(new object[] { Path.GetFileName(realPath), arq });

        RefreshDataSource();
        System.GC.Collect();
        return true;
      }
      catch (Exception exc)
      {
        MessageBox.Show("Não foi possível inserir esse arquivo. " + exc.Message, "Atenção!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
      }
    }

    public bool Remove(string fileName)
    {
      try
      {
        byte[] arq = (byte[])dtArquivos.Rows.Find(fileName)["Arquivo"];
        files.Remove(arq);
        dtArquivos.Rows.Remove(dtArquivos.Rows.Find(fileName));
        this.listView1.Items.RemoveByKey(fileName);
        RefreshDataSource();
        return true;
      }
      catch
      {
        return false;
      }
    }

    /// <summary>
    /// Exporta o arquivo
    /// </summary>
    /// <param name="fullName">Caminho mais o nome do arquivo</param>
    /// <param name="name">Nome do arquivo no listView</param>
    public void Export(string fullName, string name)
    {
      File.WriteAllBytes(fullName, (byte[])dtArquivos.Rows.Find(name)["Arquivo"]);
    }

    /// <summary>
    /// Abre um arquivo
    /// </summary>
    /// <param name="fileName">Nome mais extensão do arquivo mais " - " mais o tamnho do arquivo a ser aberto</param>
    public void Open(string fileName)
    {
      string fullPath = Path.GetTempPath() + fileName;
      File.WriteAllBytes(fullPath, (byte[])dtArquivos.Rows.Find(fileName)["Arquivo"]);
      System.Diagnostics.Process.Start(fullPath);
      if (!filesOpened.Contains(fullPath))
        filesOpened.Add(fullPath);
    }

    #endregion



    #region Eventos Criados

    //Classes EventArgs Criadas
    public class BeforeDeleteEventArgs : EventArgs
    {
      private bool cancel;
      private Arquivo file;

      public bool Cancel
      {
        get { return cancel; }
        set { cancel = value; }
      }

      public Arquivo File
      {
        get { return file; }
        set { file = value; }
      }

      public BeforeDeleteEventArgs()
      {

      }

      public BeforeDeleteEventArgs(Arquivo file)
      {
        this.file = file;
      }
    }

    public class BeforeInsertEventArgs : EventArgs
    {
      private bool cancel;

      public bool Cancel
      {
        get { return cancel; }
        set { cancel = value; }
      }
    }

    public class Arquivo
    {
      private byte[] file;
      private string fileName;

      public byte[] File
      {
        get { return file; }
        set { file = value; }
      }

      public string FileName
      {
        get { return fileName; }
        set { fileName = value; }
      }

      public Arquivo()
      {

      }

      public Arquivo(string fileName, byte[] file)
      {
        int index = fileName.LastIndexOf(" - ");
        if (index >= 0)
          this.fileName = fileName.Remove(index);
        this.file = file;
      }
    }



    //*******************************Before***********************************//
    //Delegates Before
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
    Bindable(BindableSupport.No), ReadOnly(true)]
    public delegate void beforeDelete(ToolStripButton sender, BeforeDeleteEventArgs e);

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
    Bindable(BindableSupport.No), ReadOnly(true)]
    public delegate void beforeInsert(ToolStripButton sender, BeforeInsertEventArgs e);


    //Eventos Before
    [Category("Personalizado")]
    public event beforeDelete BeforeDelete;

    [Category("Personalizado")]
    public event beforeInsert BeforeInsert;

    //*************************************************************************//




    //*******************************After*************************************//
    //Delegates After
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
    Bindable(BindableSupport.No), ReadOnly(true)]
    public delegate void afterDelete(ToolStripButton sender, EventArgs e);

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
    Bindable(BindableSupport.No), ReadOnly(true)]
    public delegate void afterInsert(ToolStripButton sender, EventArgs e);


    //Eventos After
    [Category("Personalizado")]
    public event afterDelete AfterDelete;

    [Category("Personalizado")]
    public event afterInsert AfterInsert;

    //*************************************************************************//

    #endregion

  }
}
